package cn.xzzz2020.XJynWeb.aop;

import cn.xzzz2020.XJynWeb.aop.annotation.Aspect;
import cn.xzzz2020.XJynWeb.aop.annotation.Order;
import cn.xzzz2020.XJynWeb.aop.aspect.AspectInfo;
import cn.xzzz2020.XJynWeb.aop.aspect.DefaultAspect;
import cn.xzzz2020.XJynWeb.ioc.core.BeanContainer;
import cn.xzzz2020.XJynWeb.utils.ValidationUtil;

import java.util.*;

/**
 * 对容器所有需要AOP操作的Bean，生成动态代理对象
 * @author xzzz2020
 * @version 1.0
 * @date 2020/8/4 11:10
 */
public class AspectWeaver {
    private BeanContainer beanContainer;

    public AspectWeaver() {
        beanContainer = BeanContainer.getInstance();
    }

    public void doAop() {
        //1.获取所有的切面类
        Set<Class<?>> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class);
        if (ValidationUtil.isEmpty(aspectSet)) {
            return;
        }
        //2.拼装AspectInfoList
        List<AspectInfo> aspectInfoList = packAspectInfoList(aspectSet);
        //3.遍历容器的所有类
        Set<Class<?>> classSet = beanContainer.getClasses();
        for (Class<?> targetClass : classSet) {
            //排除切面类本身
            if(!targetClass.isAnnotationPresent(Aspect.class)){
                //4.粗筛符合条件的切面
                List<AspectInfo> roughMatchedAspectList = collectRoughMatchedAspectListForSpecificClass(aspectInfoList,targetClass);
                //5.尝试进行Aspect织入
                wrapIfNecessary(roughMatchedAspectList,targetClass);
            }
        }


    }

    /**
     *
     * @param roughMatchedAspectList
     * @param targetClass
     */
    private void wrapIfNecessary(List<AspectInfo> roughMatchedAspectList, Class<?> targetClass) {
        if(!ValidationUtil.isEmpty(roughMatchedAspectList)){
            //创建动态代理对象
            AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass,roughMatchedAspectList);
            Object proxy = ProxyCreator.createProxy(targetClass, aspectListExecutor);
            beanContainer.addBean(targetClass,proxy);
        }
    }

    /**
     * 粗筛选出存在对应切点的切面逻辑
     * @param aspectInfoList 切面集合
     * @param targetClass 目标类
     * @return
     */
    private List<AspectInfo> collectRoughMatchedAspectListForSpecificClass(List<AspectInfo> aspectInfoList, Class<?> targetClass) {
        List<AspectInfo> roughMatchedAspectList = new ArrayList<>();
        for (AspectInfo aspectInfo : aspectInfoList) {
            //粗筛
            if (aspectInfo.getPointcutLocator().roughMatches(targetClass)){
                roughMatchedAspectList.add(aspectInfo);
            }
        }
        return roughMatchedAspectList;
    }

    /**
     * 验证被@Aspect注解标记的类是否符合要求
     * 如果符合要求，取出所需的Pointcut表达式，Order值以及对应的Bean
     * @param aspectSet
     * @return
     */
    private List<AspectInfo> packAspectInfoList(Set<Class<?>> aspectSet) {
        List<AspectInfo> aspectInfos = new ArrayList<>();
        for (Class<?> aspectClass : aspectSet) {
            if (verifyAspect(aspectClass)){
                Order orderTag = aspectClass.getAnnotation(Order.class);
                Aspect aspectTag = aspectClass.getAnnotation(Aspect.class);
                DefaultAspect defaultAspect = (DefaultAspect) beanContainer.getBean(aspectClass);
                //初始化表达式定位器
                PointcutLocator pointcutLocator = new PointcutLocator(aspectTag.pointcut());
                AspectInfo aspectInfo = new AspectInfo(orderTag.value(),defaultAspect,pointcutLocator);
                aspectInfos.add(aspectInfo);
            }else {
                //不遵守规范则直接抛出异常
                throw new RuntimeException("@Aspect and @Order must be added to the Aspect class, and Aspect class must extend from DefaultAspect");
            }
        }
        return aspectInfos;
    }

    //框架中一定要遵守给Aspect类添加@Aspect和@Order标签的规范，同时，必须继承自DefaultAspect.class
    //此外，@Aspect的属性值不能是它本身
    private boolean verifyAspect(Class<?> aspectClass) {
        return aspectClass.isAnnotationPresent(Aspect.class) &&
                aspectClass.isAnnotationPresent(Order.class) &&
                DefaultAspect.class.isAssignableFrom(aspectClass);
    }

}
